/*
 *  Copyright 2019-2020 heconghui
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.hcx.common.log.aspect;


import com.hcx.api.logging.dto.SysLogDto;
import com.hcx.api.logging.feign.LoggingFeignClient;
import com.hcx.common.cache.constant.CacheNames;
import com.hcx.common.cache.util.RedisUtil;
import com.hcx.common.core.until.DateUtil;
import com.hcx.common.log.annotation.IMethodLog;
import com.hcx.common.log.constant.LogConstant;
import com.hcx.common.log.dto.LogContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.text.NumberFormat;
import java.time.LocalDateTime;

/**
 * @author heconghui
 * @date 2021-11-24
 */
@Component
@Aspect
@Slf4j
public class LogAspect {
    private static final String LOG_HEADER_FLAG = "LOG_HEADER_NO";
    @Resource
    private LoggingFeignClient loggingFeignClient;

    ThreadLocal<Long> currentTime = new ThreadLocal<>();

    private static final Integer SEQ_LENGTH = 6;


    /**
     * 配置切入点
     */
    @Pointcut("@annotation(com.hcx.common.log.annotation.IMethodLog)")
    public void logPointcut() {
        // 该方法无方法体,主要为了让同类中其他方法使用此切入点
    }

    /**
     * 配置环绕通知,使用在方法logPointcut()上注册的切入点
     *
     * @param joinPoint join point for advice
     */
    @Around("logPointcut()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result;
        currentTime.set(System.currentTimeMillis());
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        IMethodLog aopLog = method.getAnnotation(IMethodLog.class);
        String reqId = getIncreaseId(aopLog);
        LogContext.put(LogConstant.REQ_ID, reqId);
        String username = "";
        log.info("--------------------------------------------------------");
        log.info("----USER:{}正在操作{}", username, aopLog.value());
        String methodName = StringUtils.join(method.getDeclaringClass().getName(), ".", method.getName(), "()");
        log.info("----METHOD:{}", methodName);
        log.info("--------------------------------------------------------");

        result = joinPoint.proceed();
        SysLogDto sysLogDto = new SysLogDto();
        sysLogDto.setId(100l);
        currentTime.remove();
        loggingFeignClient.saveLog(sysLogDto);
        return result;
    }

    private String getIncreaseId(IMethodLog aopLog) {
        String reqId = "";
        String yyyyMMddHH = DateUtil.localDateTimeFormat(LocalDateTime.now(), "yyyyMMddHH");
        String project = aopLog.project();
        String key = StringUtils.join(project, "_", LOG_HEADER_FLAG, CacheNames.UNION, yyyyMMddHH);
        Long incr = RedisUtil.incr(key, 1);
        if (incr == 1l) {
            RedisUtil.expire(key, 3600);
        }
        String seq = getSeq(incr);
        reqId = StringUtils.join(project, yyyyMMddHH, seq);
        return reqId;
    }

    private String getSeq(Long incr) {
        NumberFormat numberFormat = NumberFormat.getInstance();
        numberFormat.setGroupingUsed(false);
        numberFormat.setMaximumIntegerDigits(SEQ_LENGTH);
        numberFormat.setMinimumIntegerDigits(SEQ_LENGTH);
        return numberFormat.format(incr);
    }

    /**
     * 配置异常通知
     *
     * @param joinPoint join point for advice
     * @param e         exception
     */
    @AfterThrowing(pointcut = "logPointcut()", throwing = "e")
    public void logAfterThrowing(JoinPoint joinPoint, Throwable e) {
    }


//    public String getUsername() {
//        try {
//            return SecurityUtils.getCurrentUsername();
//        }catch (Exception e){
//            return "";
//        }
//    }
}
